package tjs.study.notes.dotest.jdk.reference;

import tjs.study.notes.dotest.jvm.heap.UserJvm;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Created by Nicky.Tang on 2021/12/15 9:12 下午
 *
 * @since 02.12.10
 */
public class DoTestOfThreadLocal {

    public static void main(String[] args) throws Exception {
//        testOnlyWeakReference();
//        testPhantomReference();
        testThreadLocal();
    }

    public static ThreadLocal<Double> TL_STATIC = new ThreadLocal<>();

    private static void testThreadLocal() throws Exception {
        Thread thread = Thread.currentThread();
        Object threadLocalMap = getThreadLocalMap(thread);//key即为ThreadLocalMap.referent属性
        
        ThreadLocal<Object> TL_NOT_STATIC = new ThreadLocal<>();
        TL_NOT_STATIC.set(new Double("1"));
        TL_STATIC.set(new Double("2"));
        System.out.println("TL_NOT_STATIC: " + TL_NOT_STATIC.get() + "; TL_STATIC:" + TL_STATIC.get());
        
        TL_NOT_STATIC = null;
        //TL_STATIC=null;
        System.gc();
        Thread.currentThread().sleep(2000);
        //threadLocalMap不存在key=TL_NOT_STATIC,但是Entry.value依然存在，需要等待rehash或set()某些情况下才会移除为空的key
        System.out.println("TL_NOT_STATIC.threadLocals: " + threadLocalMap);
        
        rehash(threadLocalMap);
        //threadLocalMap不存在Entry=TL_NOT_STATIC.value
        System.out.println("TL_NOT_STATIC.threadLocals: " + threadLocalMap);
    }

    private static void rehash(Object threadLocalMap) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException {
        Method rehashMethod = Class.forName("java.lang.ThreadLocal$ThreadLocalMap").getDeclaredMethod("rehash");
        rehashMethod.setAccessible(true);
        rehashMethod.invoke(threadLocalMap);
    }

    private static Object getThreadLocalMap(Thread thread) throws NoSuchFieldException, IllegalAccessException {
        Field threadField = Thread.class.getDeclaredField("threadLocals");
        threadField.setAccessible(true);
        return threadField.get(thread);
    }


    public static void testPhantomReference() throws Exception {
        UserJvm object = new UserJvm(1, "nicky");
        // 虚引用，必须与一个引用队列关联
        ReferenceQueue<UserJvm> queue = new ReferenceQueue();
        ConnectionHolder pr = new ConnectionHolder(object, queue);
        System.out.println("pr:" + pr.get());//get永远为null
        object = null;
        new Thread(() -> {
            try {
                ConnectionHolder obj = (ConnectionHolder) queue.remove();//自旋获取
                if (obj != null) {
                    System.out.println("queue: " + obj);//这里可以清理堆外资源
                    obj.close();
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }).start();
        while (true) {
            System.gc();
            Thread.currentThread().sleep(2000);
        }
    }

    public static class TestPhantom {

    }

    public static void testOnlyWeakReference() throws InterruptedException {
        Object obj = new Object();// 若obj为字符串常量，则不会释放
        WeakReference<Object> wf = new WeakReference<Object>(obj);// 一旦发生gc则释放
        System.out.println("new: " + wf.get());// aa
        System.gc();
        System.out.println("gc: " + wf.get());// aa(obj还可达，存在引用)
        obj = null;
        System.gc();
        System.out.println("null and gc: " + wf.get());// null
    }

    public static void testWeakReference() throws InterruptedException {
        Object d1 = new Object();
        SoftReference<Object> wf1 = new SoftReference<Object>(d1);//gc且内存不足时释放
        Object d2 = new Object();
        WeakReference<Object> wf2 = new WeakReference<Object>(d2);//一旦发生gc则释放
        System.out.println("wf1: " + wf1.get() + "; wf2:" + wf2.get());
        d1 = null;
        d2 = null;

        while (true) {
            System.gc();
            Thread.currentThread().sleep(2000);
            //wf1: java.lang.Object@246b179d; wf2:null
            System.out.println("wf1: " + wf1.get() + "; wf2:" + wf2.get());
        }
    }
}
